package gbench.appdemo.math;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import static gbench.appdemo.math.MyRecord.REC;
import static gbench.appdemo.math.Node.PACK;
import static gbench.appdemo.math.Ops.ADD;
import static gbench.appdemo.math.Ops.COS;
import static gbench.appdemo.math.Ops.DIV;
import static gbench.appdemo.math.Ops.LN;
import static gbench.appdemo.math.Ops.MUL;
import static gbench.appdemo.math.Ops.POW;
import static gbench.appdemo.math.Ops.NEG;
import static gbench.appdemo.math.Ops.SIN;
import static gbench.appdemo.math.Ops.MINUS;
import static java.text.MessageFormat.*;
import static gbench.common.matlib.MatlibCanvas.print;
import static gbench.common.matlib.MatlibCanvas.println;

import java.util.Arrays;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 
 * 代数运算/符号运算的基本演示示例
 * 
 * @author gbench
 *
 */
public class JunitAlgebra {
    
    /**
     * 语法树的生成与计算
     */
    @Test
    void foo() {
        final var engine = new AlgebraEngine();
        Stream.of(
            "5+pow(2,5)*tan(45.6/255-cos(4/6))",
            "(log(10,100)+ln(2.71828)+cot(45/89))",
            "(1+4/sin(5))",
            "1+(2+4*7-5*3+4*sin(x)+cos(x)+exp(45)/ln(x))-pow(6/9,2+sin(pow(3.14,2+1*5)))",
            "(1+log(2,3))",
            "(exp(x)/cos(x))",
            "5+pow(2,5)*tan(x)",
            "5+pow(2,5)*tan(x)+sin(x+x*3/tan(x)+5)",
            "pow(1+2*sin(x),3+6/5*(4+2))",
            "1+pow(1+2*sin(x),3)", 
            "4,3+3*5",
            "5+pow(2,3)*10",
            "(+(1,(2,(3,4+5))))",
            "(pow(2,5,4))",
            "1+4*7-6/9"
        ).forEach(line -> {
            final var root = engine.analyze(line);

            println("\n----------------------------");
            println("表达式的计算");
            println("----------------------------");
            println("语法树", root);
            println("语法树结算", root.evaluate(REC("x",10)));

            println("============================\n");
            println(line, "的语法树结构\n");
            println(root.dumpAST());
        }); // forEach
    }
    
    /**
     * 结构求导
     */
    @Test
    public void bar() {
        Stream.of(
            PACK(ADD(SIN("x"), COS("x"))),
            PACK(DIV(SIN("x"), COS("x"))),
            PACK(MUL(SIN("x"), COS("x"))),
            PACK(COS(SIN("x"))),
            PACK(MUL(1,COS(SIN("x")))),
            PACK(ADD(NEG(SIN("x")),1)),
            PACK(DIV(1,LN("x"))),
            PACK(ADD("x",DIV(1,LN("y"))))
        ).forEach(x -> {
            final var deriv_node = x.derivate();
            println("--------------------------------------");
            println("求导函数:", x);
            println("求导语法树", deriv_node);
            println("--------------------------------------");
            println(deriv_node.dumpAST());
         });
    }
    
    /**
     * 解析求导
     */
    @Test
    public void qux() {
        final var engine = new AlgebraEngine();
        Stream.of(
            "exp(x)/cos(x)",
            "tan(x)",
            "log(a,x)",
            "(sin(x)/cos(x))*(exp(x)/ln(x))",
            "sin(x)*cos(x)",
            "2*(x*x*x)+(2*x)",
            "2*pow(x+1,2)+x"
        ).forEach(line -> {
            final var node = engine.analyze(line);
            println("--------------------------------");
            println(node.dumpAST());
            final var d_node = node.derivate();
            println("--------------------------------");
            println("求导：", line);
            println("初始函数:", node);
            println("导数结构:", d_node);
            println("导数值计算 {x=1}:", d_node.evaluate(REC("x", 2)));
            println("--------------------------------");
            println(d_node.fmap(e->e.simplify()).dumpAST());
        });
    }
    
    @Test
    public void quy() {
        println("2,1,false:");
        IRecord.slidingS(Arrays.asList(1, 2, 3, 4, 5), 2, 1, false).forEach(e -> print(e));
        println("\n2,1,true:");
        IRecord.slidingS(Arrays.asList(1, 2, 3, 4, 5), 2, 1, true).forEach(e -> print(e));
        println("\n2,2,false:");
        IRecord.slidingS(Arrays.asList(1, 2, 3, 4, 5), 2, 2, true).forEach(e -> print(e));
        println("\n2,2,true:");
        IRecord.slidingS(Arrays.asList(1, 2, 3, 4, 5), 2, 2, true).forEach(e -> print(e));
    }

    @Test
    public void quz() {
        final var engine = new AlgebraEngine();
        final var node = engine.analyze("1+2*3/sin(x+pow(2,5+4))");
        println(node.dumpAST());
        println("-----------------------------");
        node.getOp().forEach((level,op)->{
            println(" | ".repeat(level)+op.getName() + " \t ---> token:" +op.isConstant()+"\t"+ op);
        });
        
        final var dfm = Stream.of(1,2,3,4,5)
            .map(e->REC("name","name"+e,"value",e))
            .collect(DFrame.dfmclc);
        println("-----------------------------");
        println(dfm);
        println(dfm.col("value"));
        println("-----------------------------");
        println("null instanceof Object",null instanceof Object);
        println("-----------------------------");
        BinaryOp.of(1, BinaryOp.of(2, 3, 4), BinaryOp.of(5, 6, 7)).flatArgsS()
        .forEach( e -> {
            print(" "+e);
        });
    }
    
    @Test
    public void rux() {
        final var engine = new AlgebraEngine();
        final var line = " 0 + 1 * sin cos tan sec x / 2 + 3 * 4 "; // 会导致失败 需要 (8*7) 加括号
        final var node = engine.analyze(line);
        println(node.dumpAST());
        println(node.evaluate(REC("x",1)));
        println(engine.analyze("x").evaluate(REC("x",1)));
    }
    
    /**
     * 泰勒级数
     */
    @Test
    public void taylor_series() {
        final var engine = new AlgebraEngine();
        final var sinx_pattern = "1/!(2*n+1)*pow(x,2*n+1)*pow(neg 1,n)"; // 通项公式
        final var cosx_pattern = "1/!(2*n)*pow(x,2*n)*pow(neg 1,n)"; // 通项公式
        final var exp_pattern = "1/!(n)*pow(x,n))"; // 通项公式
        
        println("泰勒级数的展示:sin,cos");
        final var rb = IRecord.rb("node,name,fx");
        Stream.of ( // 基础函数数据
            rb.get(engine.analyze(sinx_pattern), "sin", (Function<Double, Double>) Math::sin),
            rb.get(engine.analyze(cosx_pattern), "cos", (Function<Double, Double>) Math::cos),
            rb.get(engine.analyze(exp_pattern), "exp", (Function<Double, Double>) Math::exp)
        ).forEach(rec -> {
            final var node = rec.get("node",Node.class);
            final var name = rec.str("name"); // 函数名
            final var _name = "_" + name; // 泰勒级数名
            
            println("\n------------------------------------------------------");
            println(name, "泰勒级数的通项:");
            println(node.dumpAST());
            println("------------------------------------------------------");
            final var dfm = Stream.iterate(0, i -> i + 1).limit(10).map(t -> {
                final var x = Math.PI / (t + 1); // 计算部位
                final var _fx = Stream.iterate(0, i -> i + 1).limit(100)
                        .map(i -> node.eval(REC("n", i, "x", x)))
                        .reduce((a, b) -> a + b).get(); // 级数计算
                final var fx = rec.get("fx", (Class<Function<Double, Double>>) null).apply(x);
                
                return REC("t", t + 1, name, fx, _name, _fx, "error", fx - _fx); // 标准值比对
            }).collect(DFrame.dfmclc);
            
            println(dfm); // 打印结果数据
        }); // forEach
    }
    
    /**
     * 多项式的分析
     */
    @Test
    public void poly() {
        final var engine = new AlgebraEngine(); // 代数引擎
        
        @SuppressWarnings("unchecked")
        final Function<Number[], Node> poly = aa -> { // 多项式
            final int n = aa.length; // 长度是 最高次数 + 1
            final var expression = Stream.of(aa).map(Ops.kvp_int()).map(e -> { // (元素,索引序号：0开始)
                return format("{0}*pow(x,{1})", e._2, n - e._1);
            }).collect(Collectors.joining("+")); // 多项式的数学表达式
            final var _node = engine.analyze(expression); // 多项式的数学表达式 的 分析
            println(expression);
            println(_node);

            final var node = Stream.of(aa).map(Ops.kvp_int()).map(e -> { // (元素,索引序号：0开始)
                return (BinaryOp<Object, Object>) (Object) MUL(e._2, POW("x", n - e._1 - 1));
            }).reduce((a, b) -> ADD(a, b)).map(Node::new).get();

            return node; // 返回多项式值的值
        }; // poly
        
        final var d = poly.apply(new Number[] {1,2,3,4,5});
        println(d.dumpAST());
        println("多项式计算值:",d.eval("x",1));
        final var d_node = d.derivate();
        println(d_node.dumpAST());
        println("多项式导数计算值:",d_node.eval("x",1));
    }
    
    /**
     * 多项式化简
     */
    @Test
    public void simplify() {
        final var node = PACK(MINUS(ADD(0,MUL(1,DIV(ADD(10,MUL(1,"x")),1))),0).simplify());
        println(node.dumpAST());
        println("-----------------------------------------------");
        final var engine = new AlgebraEngine();
        final var t = engine.analyze("2*x*3*2*5*5");
        final var t2 = engine.analyze("2+5+3+2+x+5");
        println(t.fmap(e -> e.simplify()).dumpAST());
        println(t2.fmap(e -> e.simplify()).dumpAST());
        println(ADD(1,2).simplify().evaluate());
    }
    
    @BeforeAll
    static void before() {
        Ops.debug = true;
    }
}